<?php
/************************************
*   新浪相册 7.0
*   @file:lib_db.php
*   @intro:
*   @author:shaopu@
*   @date:Mon Nov 24 16:00:17 CST 2008
************************************/

class lib_db
{
	//连接池 已连接的句柄被存放于此
	public  $__conns = array();
	//当前使用的连接句柄
	public  $__conn;
	//
	private $__log_dir = 'db';
	private $__num_rows = 0;
	private $__affected_rows = 0;
	private $__insert_id = 0;
	
	public  function lib_db(){}
	
	/**
	 * 连接数据库
	 * 连接后会将句柄存入连接池，下次连接时，先查看是否已经存在，不存在再进行连接
	 *
	 * @param string $host
	 * @param string $user
	 * @param string $password
	 * @param string $db
	 * @return bool
	 */
	public function connect($host , $user , $password , $db , $alias = '')
	{
		list($host , $port) = explode(':' , $host);
		$port = is_numeric($port) ? $port : '3306';
		
	    $key = $this->__host2key($host.$port.$user.$db.$alias);
		
		//检测当前连接池中的连接是否存在且可用
		if(isset($this->__conns[$key]) && $this->_ping_db($this->__conns[$key]))
		{
			$this->__conn = $this->__conns[$key];
			return true;
		}
		
		//连接DB
		$starttime = K::microtime();
		$rs = $this->_connect($this->__conns[$key] , $host , $user , $password , $db , $port);
		$excutetime = K::microtime() - $starttime;

		//log
		if(K5_DEBUG || !$rs)
		{
		    $log = ($rs ? '' : K5_LOG_SIGN_ERROR.'connect_error'.TAB)
		           .'user:'.$user.TAB
    		       .'alias:'.$alias.TAB
    		       .'h:'.$host.':'.$port.TAB
    		       .'t:'.$excutetime;
		    $this->__log($log , 'connect');
		}
		    
		    
		if(!$rs)
		{
		    K_log::alert(K5_LOG_ALERT_DB , $alias.' '.$host.':'.$port , 'connect_err');
			return false;
		}
	    
		$this->__conn = $this->__conns[$key];
		
		return true;
	}
	
	/**
	 * 执行写操作的SQL
	 *
	 * @param string $sql
	 * @return bool
	 */
	public function query($sql)
	{
		$rs = $this->__execute($sql);
		if($rs)
		{
		    $this->__affected_rows = $this->_affected_rows();
		    $this->__insert_id = $this->_insert_id();
		
		}
		return $rs;
	}
	/**
	 * 取得写操作SQL所影响的记录行数
	 *
	 * @return int
	 */
	public function get_affected_rows()
	{
		return $this->_affected_rows();
	}
	/**
	 * get_affected_rows的别名
	 *
	 * @return unknown
	 */
	public function affected_rows()
	{
	    return $this->__affected_rows;
	}
	public function insert_id()
	{
	    return $this->__insert_id;
	}
	public function num_rows()
	{
	    return $this->__num_rows;
	}
	
	/**
	 * 根据SQL读取单个值
	 *
	 * @param string $sql
	 * @param mix $value
	 * @return bool
	 */
	public function get_var($sql , &$value)
	{
		$rs = $this->__execute($sql);
		if(!$rs)
		{
			return false;
		}
		$this->__num_rows = $this->_num_rows();
		
		
		if($this->__num_rows <= 1)
		{
			$row = $this->_fetch_row();
			$arr = array_values($row);
			$value = $arr[0];
			
			$this->_free_result();
			return true;
		}
		else
		{
		    $this->__log(K5_LOG_SIGN_ERROR.__METHOD__.': '.$sql);
		    return false;
		}
	}
	/**
     * 读取一行记录
     * 
     *
     * @param sql $sql
     * @param array $row
     * @return bool
     */
	public function get_row($sql , &$row)
	{
	    $rs = $this->__execute($sql);
		if(!$rs)
		{
			return false;
		}
		$this->__num_rows = $this->_num_rows();
		
		if($this->__num_rows <= 1)
		{
			$row = $this->_fetch_row();
			$this->_free_result();
			return true;
		}
		else
		{
		    $this->__log(K5_LOG_SIGN_ERROR.__METHOD__.': '.$sql);
		    return false;
		}
	}
	/**
	 * 读取多行记录
	 *
	 * @param sql $sql
	 * @param array $records
	 * @return bool
	 */
	public function get_records($sql , &$records)
	{
	    $rs = $this->__execute($sql);
		if(!$rs)
		{
			return false;
		}
		$this->__num_rows = $this->_num_rows();

		while ($row = $this->_fetch_row())
		{
		    $records[] = $row;
		}
		
		$this->_free_result();
		return true;
	}
	
	public function make_set_sql($array)
	{
	    $sql = '';
	    if(is_array($array) && count($array) > 0)
	    {
    	    foreach ($array as $k => $v)
    	    {
    	        $sql .= '`'.$k.'` = "'.$this->_escape($v).'",'; //mysql全是字符串
    	    }
    	    $sql = rtrim($sql , ',');
	    }
	    return $sql;
	}
	
	public function error()
	{
	    return $this->_error();
	}
	
	public function escape($str)
	{
	    return $this->_escape($str);
	}
	
	/**
	 * 根据DB HOST生成连接池的KEY
	 *
	 * @param string $host
	 * @return string
	 */
	private function __host2key($host)
	{
		return md5($host);
	}
	/**
	 * 记录DB操作日志
	 *
	 * @param string $message
	 * @param string $suffix
	 */
	private function __log($message , $suffix = '')
	{
	    
		K_log::debug($this->__log_dir, $message , $suffix );
	}
	/**
	 * 执行SQL统一入口
	 * 所有DB操作通过本方法执行，并记录LOG
	 * 
	 *
	 * @param string $sql
	 * @return bool
	 */
	private function __execute($sql)
	{
	    $this->__affected_rows = 0;
	    $this->__insert_id = 0;
	    $this->__num_rows = 0;
	    
		$starttime = K::microtime();
		$rs = $this->_execute($sql);
		$exec_time = K::microtime() - $starttime;
		
		//log
		if(K5_DEBUG || !$rs)
		{
		    $log = ($rs ? '' : K5_LOG_SIGN_ERROR.TAB)
    			   . $sql
    			   .TAB.'t:' . $exec_time
    			   .TAB . ($rs ? '' : $this->_error());
			$this->__log($log);
			
			if(!$rs)
			    K_log::alert(K5_LOG_ALERT_DB , '' , 'sql_query_error');
		}
		
		return $rs;
	}
	
	//~~以下为需要在子类中覆盖的方法~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	/**
	 * 连接数据库
	 *
	 * @param resource $conn
	 * @param string $host
	 * @param string $user
	 * @param string $password
	 * @param string $db
	 * @param int $port
	 * @return bool
	 */
	protected function _connect(&$conn , $host , $user , $password , $db , $port){}
	/**
	 * 检测数据库连接是否可用
	 *
	 * @param source $connect
	 * @return bool
	 */
	protected function _ping_db($connect){}
	/**
	 * 执行SQL
	 *
	 * @param string $sql
	 * @return bool
	 */
	protected function _execute($sql){}
	/**
	 * 取得写操作所影响的数据行数
	 *
	 * @return int
	 */
	protected function _affected_rows(){}
	/**
	 * 取得读操作所得到的行数
	 *
	 * @return int
	 */
	protected function _insert_id(){}
	/**
	 * 取得数据库报错信息
	 *
	 * @return string
	 */
	protected function _error(){}
	
	protected function _escape(){}
	
	protected function _fetch_row(){}
	
	protected function _free_result(){}
}
?>